package org.eoti.math;

import org.eoti.math.Fraction;
import org.eoti.math.cf.constants.Phi;

import java.math.BigInteger;
import java.util.Iterator;

/**
 * The MIT License
 * <p/>
 * Copyright (c) 2009 Malachi de AElfweald (malachid@gmail.com)
 * <p/>
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * <p/>
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
public class Fibonacci
implements Iterable<BigInteger>
{
	private Phi cf = new Phi();

	public Fibonacci()
	{

	}

	public static void main(String[] args)
	{
		if(args.length == 1)
		{
			Fibonacci fib = new Fibonacci();
			System.out.format("%s\n", fib.first(new BigInteger(args[0])));
		}else{
			for(BigInteger num : new Fibonacci())
				System.out.format("%s, ", num);
		}
	}

	public String first(BigInteger numValues)
	{
		StringBuilder sb = new StringBuilder();
		BigInteger count = BigInteger.ONE;
		for(BigInteger num : this)
		{
			if(count.equals(numValues))
			{
				sb.append(String.format("%s", num));
				break;
			}
			sb.append(String.format("%s, ", num));
			count = count.add(BigInteger.ONE);
		}
		return sb.toString();
	}

	/**
	 * Returns an iterator over a set of elements of type T.
	 *
	 * @return an Iterator.
	 */
	public Iterator<BigInteger> iterator(){
		return new FibonacciIterator(cf.getConvergents().iterator());
	}

	static class FibonacciIterator
	implements Iterator<BigInteger>
	{
		private Iterator<Fraction> iterator;

		protected FibonacciIterator(Iterator<Fraction> iterator)
		{
			this.iterator = iterator;
		}

		/**
		 * Returns {@code true} if the iteration has more elements. (In other words, returns {@code true} if {@link #next}
		 * would return an element rather than throwing an exception.)
		 *
		 * @return {@code true} if the iteration has more elements
		 */
		public boolean hasNext(){
			return true;
		}

		/**
		 * Returns the next element in the iteration.
		 *
		 * @return the next element in the iteration
		 *
		 * @throws java.util.NoSuchElementException
		 *          if the iteration has no more elements
		 */
		public BigInteger next()
		{
			return iterator.next().getNumerator();
		}

		/**
		 * Removes from the underlying collection the last element returned by this iterator (optional operation).  This method
		 * can be called only once per call to {@link #next}.  The behavior of an iterator is unspecified if the underlying
		 * collection is modified while the iteration is in progress in any way other than by calling this method.
		 *
		 * @throws UnsupportedOperationException if the {@code remove} operation is not supported by this iterator
		 * @throws IllegalStateException         if the {@code next} method has not yet been called, or the {@code remove}
		 *                                       method has already been called after the last call to the {@code next} method
		 */
		public void remove(){
			throw new UnsupportedOperationException("remove not supported");
		}
	}
}
